#!/bin/sh

# simulator builder script
#
# warning: this MUST NOT be called directly by the user
#
# bsc invokes
#   bsc_build_vsim_... detect
# to detect whether this simulator can be built (exit status 0 = yes)
#
# bsc invokes
#   bsc_build_vsim_... link outexe topmod clibpath clibs linkopts vdirs vdefs vopts vfiles ofiles
# where
#   outexe   - the name of the simulator executable to create
#   topmod   - the toplevel module to simulate
#   vlibpath - where to find Bluespec Verilog primitives
#   vdir     - where to find BSC-generated Verilog files
#   clibpath - where to find external C libraries
#   clibs    - which external C libraries to link in
#   linkopts - other options passed to C/C++ linker
#   vdirs    - -y directories to search for Verilog files
#   vdefs    - -D macro definitions
#   vopts    - other options passed to Verilog
#   vfiles   - the Verilog files to simulate
#   ofiles   - any VPI object files to link in
# exits with status 0 if it successfully built the simulator, 1 otherwise

BSC_COMMAND=$1           # "detect" or "link"

if [ "$BSC_COMMAND" = "detect" ]; then
# detect whether the command "verilator" is in the path
  SIM_NAME=verilator
  hash $SIM_NAME 2> /dev/null
  if [ "$?" != "0" ]; then
      echo "$SIM_NAME was not found"
      exit 1
  else
      FOUND_CMD=`which $SIM_NAME`
      echo "$SIM_NAME was found at $FOUND_CMD"
      exit 0
  fi
fi

# the only remaining command is "link"
if [ "$BSC_COMMAND" != "link" ]; then
  echo "ERROR: unknown command: $BSC_COMMAND" >&2
  exit 1
fi

BSC_SIM_EXECUTABLE=$2    # output executable filename
BSC_TOPLEVEL_MODULE=$3   # toplevel module to simulate

shift 3
BSC_VERILOG_FILES=""     # Verilog files to link
BSC_VERILOG_DIRS=""      # Verilog directories to link
BSC_VPI_FILES=""         # VPI object files to link in
BSC_C_LIB_PATH=""        # where to find C libraries for linking
BSC_C_LIBS=""            # which C files to link in
BSC_CLINK_OPTS=""        # C/C++ link options specified with -Xl in bsc
BSC_VERILOG_DEFS=""      # -D macro and -D macro=val to be passed to Verilog
BSC_VERILOG_OPTS=""      # Verilog link options specified with -Xv in bsc
BSC_OTHER_ARGUMENTS=""

# Keep a copy of the Verilog dirs, for searching
VERDIRS=""

VERBOSE="no"

USEDPI="no"

while [ $# -gt 0 ]; do
  if [ "${1%%.v}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.V}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.vo}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.sv}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.o}" != "$1" ]; then
    BSC_VPI_FILES="$BSC_VPI_FILES $1"
  elif [ "$1" = "-L" ]; then
    shift 1
    BSC_C_LIB_PATH="$BSC_C_LIB_PATH -L$1"
  elif [ "$1" = "-y" ]; then
    shift 1
    # Also search for Verilog include files in these directories
    BSC_VERILOG_DIRS="$BSC_VERILOG_DIRS -y $1"
    VERDIRS="$VERDIRS $1"
  elif [ "$1" = "-l" ]; then
    shift 1
    BSC_C_LIBS="$BSC_C_LIBS -l$1"
  elif [ "$1" = "-Xl" ]; then
    shift 1
    BSC_CLINK_OPTS="$BSC_CLINK_OPTS $1"
  elif [ "$1" = "-D" ]; then
    shift 1
    BSC_VERILOG_DEFS="$BSC_VERILOG_DEFS -D$1"
  elif [ "$1" = "-Xv" ]; then
    shift 1
    BSC_VERILOG_OPTS="$BSC_VERILOG_OPTS $1"
  elif [ "$1" = "-verbose" ]; then
    VERBOSE="yes"
  elif [ "$1" = "-dpi" ]; then
    USEDPI="yes"
  else
    BSC_OTHER_ARGUMENTS="$BSC_OTHER_ARGUMENTS $1";
  fi
  shift 1
done

if [ -z "$BSC_SIM_EXECUTABLE" ]; then
  echo "ERROR: simulator executable filename not specified"
  exit 1
fi

if [ -z "$BSC_TOPLEVEL_MODULE" ]; then
  echo "ERROR: no top-level module specified" >&2
  exit 1
fi

if [ -z "$BSC_VERILOG_FILES" ]; then
  echo "ERROR: no Verilog files to link" >&2
  exit 1
fi

if [ -n "$BSC_OTHER_ARGUMENTS" ]; then
  echo "ERROR: unrecognized arguments '$BSC_OTHER_ARGUMENTS'" >&2
  exit 1
fi

# Ignore 'main' from BSC_VERILOG_FILES, and record if seen.
# And remove the top module, if provided, to avoid passing
# it multiple times to Verilator.
#
BSC_VERILOG_FILES_NO_MAIN=""
HAS_MAIN=0
for f in $BSC_VERILOG_FILES
do
    fbase=`basename $f`
    if [ "$fbase" = "main.v" ] ; then
	HAS_MAIN=1
    else
	if [ "$fbase" != "${BSC_TOPLEVEL_MODULE}.v" ] ; then
	    BSC_VERILOG_FILES_NO_MAIN="$BSC_VERILOG_FILES_NO_MAIN $f"
	fi
    fi
done
# XXX We should support calling without a main
# XXX (see "verilog_link_no_main" in the testsuite)
# XXX and in that case use a toplevel cpp file that doesn't
# XXX provide clock/reset etc

# If foreign function object files are provided,
# check that a DPI declarations file was also provided
VPI_FILES=""
if [ -n "$BSC_VPI_FILES" ]; then
    if [ "$USEDPI" = "no" ] ; then
	echo "ERROR: Verilator does not support VPI functions/tasks."
	echo "       Compile and link with the -use-dpi flag."
	exit 1
    fi
    # Verilator's Makefile doesn't adjust relative paths for object files
    # so pass them with absolute paths
    for f in $BSC_VPI_FILES
    do
	case $f in
	    /*) VPI_FILES="$VPI_FILES $f" ;;
	    *)  VPI_FILES="$VPI_FILES $PWD/$f" ;;
	esac
    done
fi

# path to Verilog files
VSIM_PATH_FLAGS=$BSC_VERILOG_DIRS

TRACEFLAGS="--trace"

# Don't rely on the top module file being the first on the command-line
TOPMOD=${BSC_TOPLEVEL_MODULE}
VLT_TOP="--top-module ${TOPMOD}"
TOPINST="top"
VLT_TOPINST="-l2-name ${TOPINST}"
TOPCLASS=V${TOPMOD}
VLT_PREFIX="--prefix ${TOPCLASS}"

#VLT_WARN="-Wall"
#VLT_WARN="-Wall -Wno-fatal"
VLT_WARN=""

VLT_CONFIG=$BLUESPECDIR/Verilator/verilator_config.vlt

# Starting with version 5, zero-delay statements (like #0) require
# a flag to specify how they should be handled (--timing or --no-timing)
MAJVER=`verilator --version 2>/dev/null | head -n 1 | grep -o -E "^Verilator [0-9]+\." | grep -o -E "[0-9]+"`
if [ "$MAJVER" -ge "5" ]; then
    VLT_TIMING="--no-timing"
else
    VLT_TIMING=""
fi

# Uniquify the obj_dir with the name of the sim executable and the PID
# XXX is the PID overkill?
EXE_BASENAME=`basename ${BSC_SIM_EXECUTABLE}`
OBJDIR="obj_dir_${EXE_BASENAME}_$$"

# If the directory happens to exit, delete it
# to avoid accidentally reusing old files
rm -rf $OBJDIR

# XXX We could also delete the directory on exit?
#function cleanup {
#    rm -rf $OBJDIR
#}
#trap cleanup EXIT

# Find the top file
# For instantiated submodules, Verilator can search in a path to find
# the file; but Verilator doesn't do this for the top module.  We have
# to specify that file on the command-line.
TOP_VERFILE=""
for d in $VERDIRS ; do
    if [ -f ${d}/${BSC_TOPLEVEL_MODULE}.v ] ; then
	TOP_VERFILE=${d}/${BSC_TOPLEVEL_MODULE}.v
	break
    fi
done
if [ -z "$TOP_VERFILE" ]; then
    echo "ERROR: Verilog file not found for top module '${BSC_TOPLEVEL_MODULE}'"
    exit 1
fi


# compile Verilog files
if [ "$VERBOSE" = "yes" ]; then
  # XXX Verilator doesn't have a verbosity flag?
  VLT_VERBOSE=""
  echo "exec: verilator $VLT_VERBOSE --Mdir $OBJDIR $VLT_WARN --cc --exe sim_main.cpp -CFLAGS \"-DTOP=$BSC_TOPLEVEL_MODULE $BSC_VERILOG_DEFS\" $VLT_TOP $VLT_TOPINST $VLT_PREFIX $TRACEFLAGS $VSIM_PATH_FLAGS $BSC_VSIM_FLAGS -DTOP=$BSC_TOPLEVEL_MODULE $BSC_VERILOG_DEFS $VLT_TIMING $BSC_VERILOG_OPTS $TOP_VERFILE $VLT_CONFIG $BSC_VERILOG_FILES_NO_MAIN $VPI_FILES -LDFLAGS \"$BSC_C_LIB_PATH $BSC_CLINK_OPTS $BSC_C_LIBS\""
else
  VLT_VERBOSE=""
fi

verilator $VLT_VERBOSE --Mdir $OBJDIR $VLT_WARN --cc --exe sim_main.cpp -CFLAGS "-DTOP=$BSC_TOPLEVEL_MODULE $BSC_VERILOG_DEFS" $VLT_TOP $VLT_TOPINST $VLT_PREFIX $TRACEFLAGS $VSIM_PATH_FLAGS $BSC_VSIM_FLAGS -DTOP=$BSC_TOPLEVEL_MODULE $BSC_VERILOG_DEFS $VLT_TIMING $BSC_VERILOG_OPTS $TOP_VERFILE $VLT_CONFIG $BSC_VERILOG_FILES_NO_MAIN $VPI_FILES -LDFLAGS "$BSC_C_LIB_PATH $BSC_CLINK_OPTS $BSC_C_LIBS"

status=$?
if [ "$status" != "0" ]; then
  echo "ERROR: cannot compile Verilog files" >&2
  exit $status
fi

cp -p $BLUESPECDIR/Verilator/sim_main.cpp $OBJDIR/sim_main.cpp

make -C $OBJDIR -j -f ${TOPCLASS}.mk ${TOPCLASS}

status=$?
if [ "$status" != "0" ]; then
  echo "ERROR: cannot link Verilator files" >&2
  exit $status
fi

cp -p $OBJDIR/${TOPCLASS} $BSC_SIM_EXECUTABLE

# Remove the work directory
rm -rf $OBJDIR

exit 0
